/**
 * 
 */
package com.et114.modules.extend.acegi;


import org.acegisecurity.AcegiMessageSource;
import org.acegisecurity.Authentication;
import org.acegisecurity.AuthenticationException;
import org.acegisecurity.BadCredentialsException;
import org.acegisecurity.providers.AuthenticationProvider;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.providers.cas.CasAuthenticationProvider;
import org.acegisecurity.providers.cas.CasAuthenticationToken;
import org.acegisecurity.providers.cas.CasProxyDecider;
import org.acegisecurity.providers.cas.StatelessTicketCache;
import org.acegisecurity.providers.cas.TicketResponse;
import org.acegisecurity.providers.cas.TicketValidator;
import org.acegisecurity.providers.cas.cache.NullStatelessTicketCache;
import org.acegisecurity.ui.cas.CasProcessingFilter;
import org.acegisecurity.userdetails.UserDetails;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.MessageSource;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.util.Assert;

import com.et114.core.utility.ArgumentTokenizer;
import com.et114.core.utility.StringPool;
import com.et114.modules.org.services.UserManager;
import com.et114.modules.org.vo.UserVo;

/**
 * @author guanhw
 */
public class AwCasAuthenticationProvider implements AuthenticationProvider, InitializingBean, MessageSourceAware {
	
	protected static final Log logger = LogFactory.getLog(CasAuthenticationProvider.class);

    //~ Instance fields ================================================================================================

	
	private AwCasAuthoritiesPopulator	casAuthoritiesPopulator;
	private CasProxyDecider				casProxyDecider;
	protected MessageSourceAccessor		messages				= AcegiMessageSource.getAccessor ( );
	private StatelessTicketCache		statelessTicketCache	= new NullStatelessTicketCache ( );
	private String						key;
	private TicketValidator				ticketValidator;
	private AwBean						bean                    = new AwBean( );
	private IAwCasPrincipalConvert		casConvert;
	private UserManager					userManager;
	private UserVo						currentUser;

    public AwCasAuthenticationProvider( ) {}
    //~ Methods ========================================================================================================

	public void afterPropertiesSet() throws Exception {
        Assert.notNull(this.casAuthoritiesPopulator, "A casAuthoritiesPopulator must be set");
        Assert.notNull(this.ticketValidator, "A ticketValidator must be set");
        Assert.notNull(this.casProxyDecider, "A casProxyDecider must be set");
        Assert.notNull(this.statelessTicketCache, "A statelessTicketCache must be set");
        Assert.hasText(this.key, "A Key is required so CasAuthenticationProvider can identify tokens it previously authenticated");
        Assert.notNull(this.messages, "A message source must be set");
    }

    public Authentication authenticate(Authentication authentication)
        throws AuthenticationException {
        if (!supports(authentication.getClass())) {
            return null;
        }
        if (authentication instanceof UsernamePasswordAuthenticationToken
            && (!CasProcessingFilter.CAS_STATEFUL_IDENTIFIER.equals(authentication.getPrincipal().toString())
            && !CasProcessingFilter.CAS_STATELESS_IDENTIFIER.equals(authentication.getPrincipal().toString()))) {
            // UsernamePasswordAuthenticationToken not CAS related
            return null;
        }
        // If an existing CasAuthenticationToken, just check we created it
        if (authentication instanceof CasAuthenticationToken) {
            if (this.key.hashCode() == ((CasAuthenticationToken) authentication).getKeyHash()) {
                return authentication;
            } else {
                throw new BadCredentialsException(messages.getMessage("CasAuthenticationProvider.incorrectKey",
                        "The presented CasAuthenticationToken does not contain the expected key"));
            }
        }

        // Ensure credentials are presented
        if ((authentication.getCredentials() == null) || "".equals(authentication.getCredentials())) {
            throw new BadCredentialsException(messages.getMessage("CasAuthenticationProvider.noServiceTicket",
                    "Failed to provide a CAS service ticket to validate"));
        }

        boolean stateless = false;

        if (authentication instanceof UsernamePasswordAuthenticationToken
            && CasProcessingFilter.CAS_STATELESS_IDENTIFIER.equals(authentication.getPrincipal())) {
            stateless = true;
        }

        CasAuthenticationToken result = null;

        if (stateless) {
            // Try to obtain from cache
            result = statelessTicketCache.getByTicketId(authentication.getCredentials().toString());
        }
 
        if (result == null) {
            result = authenticateNow(authentication);
            if ( result == null ) return  null ;
            result.setDetails(authentication.getDetails());
        }

        if (stateless) {
            // Add to cache
            statelessTicketCache.putTicketInCache(result);
        }

        return result;
    }

    
	protected CasAuthenticationToken authenticateNow (	Authentication authentication ) throws AuthenticationException {		
		long ct = System.currentTimeMillis ( ) ; 
		TicketResponse response = getTicketValidator ( ).confirmTicketValid ( authentication.getCredentials ( ).toString ( ) );
		String currentPrincipal = response.getUser ( ) ; 
		UserDetails userDetails = null ; 
		String currentSystem = Aw4UsedcarConfig.getConfig ( ).getString (  AwConst.CURRENT_SYSTEM_KEY ) ;
		logger.info ( "CURRENT_SYSTEM_KEY." + currentSystem ) ;
		logger.info ( "currentPrincipal." + currentPrincipal ) ;	
	
		if ( currentPrincipal.indexOf ( AwConst.SSO_LOGIN_PARAMS ) > 0   ) {
			String params = currentPrincipal.substring ( currentPrincipal
					.indexOf ( AwConst.SSO_LOGIN_PARAMS )
					+ AwConst.SSO_LOGIN_PARAMS.length ( ) + 1 ,
					currentPrincipal.length ( ) );
			
			currentPrincipal = currentPrincipal.substring(  0 , currentPrincipal.indexOf( AwConst.SSO_LOGIN_PARAMS ) ) ;
			
			logger.info ( AwConst.SSO_LOGIN_PARAMS + ":" + params ) ; 
			
			ArgumentTokenizer ats = new ArgumentTokenizer( params , ';' ) ;
			while( ats.hasNext ( ) ) {
				String at = ats.next ( ) ; 
				if ( at.indexOf ( StringPool.COMMA ) > 0 ) {
					String key = at.substring ( 0 , at.indexOf ( StringPool.COMMA ) ) ; 
					String value = at.substring ( at.indexOf ( StringPool.COMMA ) + 1 , at.length ( ) ) ; 
					if ( AwConst.SSO_LOGIN_TYPE_PARAM.equals ( key ) ) {
						if ( !AwConst.SSO_LOGIN_TYPE_CODE.equals ( value ) && !AwConst.SSO_LOGIN_TYPE_LG.equals ( value ) ) {
							logger.error ( "SSO_LOGIN_TYPE  Not Support  " + value ) ; 
							return null ; 
						}
						bean.setLoginType ( value ) ; 
					}
					if ( AwConst.ACEGI_SECURITY_SUCCESS_URL_METHOD.equals ( key ) ) {
						bean.setUrlTarget ( value ) ; 
					}
					if ( AwConst.SSO_LOGIN_TYPE_CODE.equals ( key  ) ) {
						bean.setCode ( value ) ;
					}
				}
			}
		}
		
		
		logger.info ( "SSO_LOGIN_TYPE." + bean.getLoginType ( ) ) ;		

		
		if ( AwConst.SYSTEM_LAS.equalsIgnoreCase ( currentSystem ) ) {
			if ( AwConst.SSO_LOGIN_TYPE_CODE.equals ( bean.getLoginType ( ) ) ) {
				if (  currentPrincipal == null  ) return null ;
					
				// ReLookup user details
				logger.info ( "convert successfull !" ) ; 				
			}
			else if ( AwConst.SSO_LOGIN_TYPE_LG.equals ( bean.getLoginType ( ) ) ){
				currentPrincipal = bean.getCode ( ) ; 
			}
			userDetails = getCasAuthoritiesPopulator ( ).getUserDetails ( currentPrincipal ); 			
		}
		
		else if ( AwConst.SYSTEM_UAW.equalsIgnoreCase ( currentSystem ) ) {
			if ( AwConst.SSO_LOGIN_TYPE_CODE.equals ( bean.getLoginType ( ) )   ) {
				//转code 为用户
				logger.info ( "convert begin !" ) ; 
				if ( casConvert == null || currentPrincipal == null ) return null ;
				UserVo user = casConvert.convert ( currentPrincipal ) ;
				if ( user == null ) {
					logger.error ( "authenticateNow error , Not Found currentPrincipal" ) ; 
					return null ;
				}
				setCurrentUser ( user ) ; 
				currentPrincipal = user.getLoginName ( ) ; 
				logger.info ( "convert successfull !" ) ; 
			}
			//
			userDetails = getCasAuthoritiesPopulator ( ).getUserDetails ( currentPrincipal ) ;
			if ( userDetails == null )
				userDetails = getCasAuthoritiesPopulator ( ).getUserDetailsDb ( currentPrincipal ) ;
		}
		else {
			userDetails = getCasAuthoritiesPopulator ( ).getUserDetails ( currentPrincipal ) ;
		}
		
		
	
		for ( int i = 0 ; i < userDetails.getAuthorities ( ).length ; i ++ ) {
			logger.info ( "currentPrincipal::" + currentPrincipal + ".." + userDetails.getAuthorities ( ) [ i ].getAuthority ( ) );
		}		
	
		if ( userDetails == null ) {
			userDetails = getCasAuthoritiesPopulator ( ).getUserDetailsDb ( currentPrincipal ) ; 
		}
		
		// Check proxy list is trusted
		getCasProxyDecider ( ).confirmProxyListTrusted ( response.getProxyList ( ) );
		
		
		logger.info ( "CAS authenticateNow :"  + ( System.currentTimeMillis ( ) - ct ) + "ms" ) ; 
		// Construct CasAuthenticationToken
		logger.info ( "CAS :" + authentication.getCredentials ( ) ) ; 
		return new CasAuthenticationToken ( getKey ( ) , userDetails ,
				authentication.getCredentials ( ) , userDetails
						.getAuthorities ( ) , userDetails , response
						.getProxyList ( ) , response
						.getProxyGrantingTicketIou ( ) );
	}    


  

    public CasProxyDecider getCasProxyDecider() {
        return casProxyDecider;
    }

    public String getKey() {
        return key;
    }

    public StatelessTicketCache getStatelessTicketCache() {
        return statelessTicketCache;
    }

    public TicketValidator getTicketValidator() {
        return ticketValidator;
    }

    public AwCasAuthoritiesPopulator getCasAuthoritiesPopulator ( ) {
		return casAuthoritiesPopulator;
	}

	public void setCasAuthoritiesPopulator (
			AwCasAuthoritiesPopulator casAuthoritiesPopulator ) {
		this.casAuthoritiesPopulator = casAuthoritiesPopulator;
	}

	public void setCasProxyDecider(CasProxyDecider casProxyDecider) {
        this.casProxyDecider = casProxyDecider;
    }

    public void setKey(String key) {
        this.key = key;
    }

    public void setMessageSource(MessageSource messageSource) {
        this.messages = new MessageSourceAccessor(messageSource);
    }

    public void setStatelessTicketCache(StatelessTicketCache statelessTicketCache) {
        this.statelessTicketCache = statelessTicketCache;
    }

    public void setTicketValidator(TicketValidator ticketValidator) {
        this.ticketValidator = ticketValidator;
    }

    public boolean supports(Class authentication) {
        if (UsernamePasswordAuthenticationToken.class.isAssignableFrom(authentication)) {
            return true;
        } else if (CasAuthenticationToken.class.isAssignableFrom(authentication)) {
            return true;
        } else {
            return false;
        }
    }	

	public AwBean getBean ( ) {
		return bean;
	}

	public void setBean ( AwBean bean ) {
		this.bean = bean;
	}

	public IAwCasPrincipalConvert getCasConvert ( ) {
		return casConvert;
	}

	public void setCasConvert ( IAwCasPrincipalConvert casConvert ) {
		this.casConvert = casConvert;
	}

	public UserManager getUserManager ( ) {
		return userManager;
	}

	public void setUserManager ( UserManager userManager ) {
		this.userManager = userManager;
	}

	public UserVo getCurrentUser ( ) {
		return currentUser;
	}

	public void setCurrentUser ( UserVo currentUser ) {
		this.currentUser = currentUser;
	}
}
